#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winsock2.h>
#include <process.h>

#define BUF_SIZE 2048
#define BUF_SMALL 100

unsigned WINAPI request_handler(void *arg);
void send_data(SOCKET sock, char *ct, char *file_name);
char *content_type(char *file);
void send_error(SOCKET sock);
void error_handling(char *message);

int main(int argc, char *argv[])
{
    WSADATA wsaData;
    SOCKET serv_sock, clnt_sock;
    SOCKADDR_IN serv_adr, clnt_adr;
    int clnt_adr_size;
    DWORD dwThreadID;
    HANDLE hThread;

    if (argc != 2)
    {
        printf("Usage : %s <port>\n", argv[0]);
        exit(1);
    }

    if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0)
    {
        error_handling("WSAStartup() error.");
    }

    serv_sock = socket(PF_INET, SOCK_STREAM, 0);
    memset(&serv_adr, 0, sizeof(serv_adr));
    serv_adr.sin_family = AF_INET;
    serv_adr.sin_addr.s_addr = htonl(INADDR_ANY);
    serv_adr.sin_port = htons(atoi(argv[1]));

    if (bind(serv_sock, (SOCKADDR *)&serv_adr, sizeof(serv_adr)) == SOCKET_ERROR)
        error_handling("bind() error");

    if (listen(serv_sock, 5) == SOCKET_ERROR)
        error_handling("listen() error");

    while (1)
    {
        clnt_adr_size = sizeof(clnt_adr);
        clnt_sock = accept(serv_sock, (SOCKADDR *)&clnt_adr, &clnt_adr_size);
        printf("Connection Request : %s:%d\n",
               inet_ntoa(clnt_adr.sin_addr), ntohs(clnt_adr.sin_port));
        // 为每个连接客户端建立创建线程
        hThread = (HANDLE)_beginthreadex(
            NULL, 0, request_handler, (void *)clnt_sock, 0, (unsigned *)&dwThreadID);
    }
    closesocket(serv_sock);
    WSACleanup();
    return 0;
}

unsigned WINAPI request_handler(void *arg)
{
    SOCKET clnt_sock = (SOCKET)arg;
    char req_line[BUF_SIZE];
    char method[BUF_SMALL];
    char ct[BUF_SMALL];
    char file_name[BUF_SMALL];

    recv(clnt_sock, req_line, BUF_SIZE, 0);
    if (strstr(req_line, "HTTP/") == NULL)
    {
        send_error(clnt_sock);
        closesocket(clnt_sock);
        return 1;
    }

    strcpy(method, strtok(req_line, " /"));
    if (strcmp(method, "GET"))
        send_error(clnt_sock);

    strcpy(file_name, strtok(NULL, " /"));
    strcpy(ct, content_type(file_name));
    send_data(clnt_sock, ct, file_name);
    return 0;
}
void send_data(SOCKET sock, char *ct, char *file_name)
{
    // chrome不再支持http协议
    char protocol[] = "HTTP/1.1 200 OK\r\n";
    char server[] = "Server:simple web server \r\n";
    char cnt_len[] = "Content-length:2048\r\n";
    char cnt_type[BUF_SMALL];
    char buf[BUF_SIZE];
    FILE *send_file;

    sprintf(cnt_type, "Content-type:%s\r\n\r\n", ct);
    if ((send_file = fopen(file_name, "r")) == NULL)
    {
        send_error(sock);
        return;
    }

    //传输头信息
    send(sock, protocol, strlen(protocol), 0);
    send(sock, server, strlen(server), 0);
    send(sock, cnt_len, strlen(cnt_len), 0);
    send(sock, cnt_type, strlen(cnt_type), 0);

    //传输请求数据
    while (fgets(buf, BUF_SIZE, send_file) != NULL)
        send(sock, buf, strlen(buf), 0);

    closesocket(sock);
}

void send_error(SOCKET sock)
{
    // chrome不再支持http协议
    char protocol[] = "HTTP/1.1 400 Bad Request\r\n";
    char server[] = "Server:simple web server \r\n";
    char cnt_len[] = "Content-length:2048\r\n";
    char cnt_type[] = "Content-type:text/html;charset=UTF-8\r\n\r\n";
    char content[] = "<html><head><title>NETWORK</title></head>"
                     "<body><font size=+5><br>发生错误！ 查看请求文件名和请求方式!"
                     "</font></body></html>";

    send(sock, protocol, strlen(protocol), 0);
    send(sock, server, strlen(server), 0);
    send(sock, cnt_len, strlen(cnt_len), 0);
    send(sock, cnt_type, strlen(cnt_type), 0);
    send(sock, content, strlen(content), 0);
    closesocket(sock);
}

char *content_type(char *file)
{
    char extension[BUF_SMALL];
    char file_name[BUF_SMALL];
    strcpy(file_name, file);
    strtok(file_name, ".");
    strcpy(extension, strtok(NULL, "."));

    if (!strcmp(extension, "html") || !strcmp(extension, "htm"))
        return "text/html";
    else
        return "text/plain";
}

void error_handling(char *message)
{
    fputs(message, stderr);
    fputc('\n', stderr);
    exit(1);
}